/*
 *  Copyright 2012 yura.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.odg.dg.logicbeans;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.opu.odg.dg.incomingdata.ContentDescription;
import org.opu.odg.dg.question.QuestionsForDocGeneration;
import org.opu.odg.dg.util.XmlSerializator;
import org.opu.odg.dg.util.ArchiveUtil;

/**
 * In this moment template for as is only it content and it connection (id)
 * with it storage.
 * @author yura
 */
public class Template implements Serializable{

    private Integer id;
    private byte[] templateBinaryData;

    private boolean wasInited = false;
    private boolean dirtyData = false;

    private TemplateFile templateFile;
    private TemplateMetaInf metaInf;
    private QuestionsForDocGeneration questions;

    public Template(Integer id, byte[] templateData) {
        this.id = id;
        this.templateBinaryData = templateData;
    }

    public Template() {
        this(null, new byte[]{});
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public byte[] getTemplateBinaryData() throws IOException {
        if (dirtyData) {
            Map<String, byte[]> map = ArchiveUtil.readArchive(templateBinaryData);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();

            templateFile.store(bout);
            map.put(TemplateMetaInf.TEMPLATE_XML, bout.toByteArray());
            bout = new ByteArrayOutputStream();

            XmlSerializator.write(metaInf, bout);
            map.put(TemplateMetaInf.METAINF_XML, bout.toByteArray());
            bout = new ByteArrayOutputStream();
            
            XmlSerializator.write(questions, bout);
            map.put(QuestionsForDocGeneration.QUESTIONS_XML, bout.toByteArray());
            bout = new ByteArrayOutputStream();

            ArchiveUtil.writeArchive(map, bout);
            templateBinaryData = bout.toByteArray();
            dirtyData = false;
        }

        return templateBinaryData;
    }

    public void setTemplateBinaryData(byte[] templateData) {
        this.templateBinaryData = templateData;
    }

    public void setResolvedFileContent(Map<String, String> data) {
        templateFile.setResolvedFileContent(data);
        setTemplateFile(templateFile);
    }

    public void setTemplateMetaInf(TemplateMetaInf templateMetaInf) {
        metaInf = templateMetaInf;
        dirtyData = true;
    }

    public void setTemplateFile(TemplateFile templateData) {
        this.templateFile = templateData;
        dirtyData = true;
    }

    public void setTemplateQuestions(QuestionsForDocGeneration questions) {
        this.questions = questions;
        dirtyData = true;
    }

    public Map<String, String> getTemplateFileContent() throws IOException {
        initIfWasntInited();
        return templateFile.getTemplateFileContent();
    }

    public Map<String, ContentDescription> getContents() throws IOException {
        initIfWasntInited();
        return metaInf.getContents();
    }

    public TemplateMetaInf getMetaInf() throws IOException {
        initIfWasntInited();
        return metaInf;
    }

    public TemplateFile getTemplateFile() throws IOException {
        initIfWasntInited();
        return templateFile;
    }

    public QuestionsForDocGeneration getTemplateQuestions() throws IOException {
        initIfWasntInited();
        return questions;
    }

    private void initIfWasntInited() throws IllegalArgumentException, IOException {
        if (!wasInited && !dirtyData) {
            init();
        }
    }

    private void init() throws IOException, IllegalArgumentException {
        Map<String, byte[]> archive = ArchiveUtil.readArchive(
                new ByteArrayInputStream(templateBinaryData));
        metaInf = XmlSerializator.read(TemplateMetaInf.class,
                getWithCheck(archive, TemplateMetaInf.METAINF_XML));

        questions = XmlSerializator.readSafe(QuestionsForDocGeneration.class,
                getWithCheck(archive, QuestionsForDocGeneration.QUESTIONS_XML));

        // TODO: Replace with factory
        switch (metaInf.getType()) {
            case ArchiveFile:
                templateFile = new ArchiveFileTemplateForDG(
                        metaInf.getEntryDeterminator());
                break;
            case SingleFile:
                templateFile = new SingleFileTemplateForDG();
                break;
            default:
                throw new IllegalArgumentException("Unsupported template type: "
                        + metaInf.getType());
        }
        
        templateFile.load(new ByteArrayInputStream(
                archive.get(TemplateMetaInf.TEMPLATE_XML)));

        wasInited = true;
    }

    private byte[] getWithCheck(Map<String, byte[]> archive, String id) {
        if (!archive.containsKey(id)) {
            throw new IllegalArgumentException("Template archive doesn't "
                    + "contain " + id + ", that is critical for template.");
        }
        return archive.get(id);
    }

}
